﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FernUniHagen.CompilerExtension.DataStructures;
using FernUniHagen.CompilerExtension.Functions;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using Roslyn.Services.CSharp;
using Roslyn.Compilers;
using FernUniHagen.CompilerExtension.Attributes;
using FernUniHagen.CompilerExtension.Converter.Block;

namespace FernUniHagen.CompilerExtension.Converter
{
    public class BlockSyntaxConverter : NodeConverterFactory
    {
        private List<SyntaxNodeInfo> _nodesToConvertInThisBlock;

        private ISemanticModel _semanticModel;

        public BlockSyntaxConverter(ISemanticModel semanticModel, List<SyntaxNodeInfo> nodeInfos)
        {
            _nodesToConvertInThisBlock = nodeInfos;
            _semanticModel = semanticModel;
        }

        public override CommonSyntaxNode Convert()
        {
            var blockSyntax = Utilities.CastNode<BlockSyntax>(Node);

            var newBlock = Syntax.Block();
           _nodesToConvertInThisBlock.Sort(delegate(SyntaxNodeInfo a, SyntaxNodeInfo b)
            {
                return a.ReferencedNode.GetLocation().GetLineSpan(true).StartLinePosition.Line.CompareTo(b.ReferencedNode.GetLocation().GetLineSpan(true).StartLinePosition.Line);
            });

           var allBlockNodes = blockSyntax.ChildNodes();
           
           foreach (var blockNode in allBlockNodes)
           {
                //@TEXT:will store replacement rules for the statement (the original statement)
                List<StatementReplacement<CommonSyntaxNode>> replacements = new List<StatementReplacement<CommonSyntaxNode>>();
                //@TEXT:will store statements that will be added before the original statement (Roslyn can not replace a statement with several other statements)
                var preStatements = new List<StatementSyntax>();

                IEnumerable<SyntaxNodeInfo> expressionsToReplace = null;
                //TODO: Check with reflector what IsEquivalentTo does. Probably it is only a string compare
                expressionsToReplace = _nodesToConvertInThisBlock.Where(n => n.Statement.IsEquivalentTo(blockNode));
                expressionsToReplace = expressionsToReplace.Distinct();
                

                //TODO:So far only the firstordefault node is taken. what happens with the other stuff?
                if (expressionsToReplace.Count() > 0) { 
                    //First create all the statements we need to add before the current statement
                    foreach (SyntaxNodeInfo expressionToReplace in expressionsToReplace)
                    {
                       try
                       {
                            preStatements.AddRange(ConvertNode(newBlock, expressionToReplace, replacements));
                       }
                       catch (Exception ex)
                       {
                           
                           newBlock = newBlock.AddStatements((StatementSyntax)blockNode);
                           Console.WriteLine(string.Format("Could not convert the node: {0}\nWith {1} Converter", expressionToReplace.ReferencedNode, this.ToString()));
                       }
                    }

                    //Then replace the expressions that need to be replaced in the current statement, if there are any replacements
                    var newStatement = ((StatementSyntax)blockNode);
                    if (replacements != null && replacements.Count > 0) { 
                        foreach (var replacement in replacements)
                        {
                            //Verify that the oldnode is equivalent to the node in the statement
                            var oldSyntaxNode = ((StatementSyntax)blockNode).DescendantNodesAndSelf().Where(n => n.IsEquivalentTo(replacement.OldSyntaxNode)).First();
                            replacement.OldSyntaxNode = oldSyntaxNode;
                        }
                         var oldSyntaxNodes = replacements.Select(n => n.OldSyntaxNode).ToList();
                        newStatement = ((StatementSyntax)blockNode).ReplaceNodes(oldSyntaxNodes, computeReplacementNode: (OldNode, newNode) =>
                        {
                            StatementReplacement<CommonSyntaxNode> replacement = replacements.First(n => n.OldSyntaxNode == OldNode);
                            return replacement.NewSyntaxNode;
                        }
                       );
                    }


                    //Finally take care of the trivia

                    //MethodInvocation might erase the original statement
                    if (newStatement != null) { 
                        newStatement = newStatement.WithLeadingTrivia(Syntax.Space);
                        newStatement = newStatement.WithTrailingTrivia(blockNode.GetTrailingTrivia());
                    }else
                        preStatements[preStatements.Count - 1].WithTrailingTrivia(blockNode.GetTrailingTrivia());
                    

                    if (preStatements.Count > 0 ) { 
                        preStatements[0] = preStatements.First().WithLeadingTrivia(blockNode.GetLeadingTrivia());
                        
                    }
                    else
                    {
                        newStatement = newStatement.WithLeadingTrivia(blockNode.GetLeadingTrivia());
                    }

                    //and add the statements
                    newBlock = newBlock.AddStatements(preStatements.ToArray());
                    if(newStatement != null)
                        newBlock = newBlock.AddStatements(newStatement);
                }
                else
                {
                    newBlock = newBlock.AddStatements((StatementSyntax)blockNode);
                }
            }
           return newBlock;
        }

        private List<StatementSyntax> ConvertNode(BlockSyntax newBlock, SyntaxNodeInfo nodeToReplace, List<StatementReplacement<CommonSyntaxNode>> statementReplacements)
        {
            List<StatementSyntax> statements = new List<StatementSyntax>();
            var originalStatement = nodeToReplace.Statement;


            //Get the correct converter depending on the statement and convert the node
            var blockNodeFacInstance = BlockNodeFactory.GetConverter(_semanticModel, newBlock, nodeToReplace);
            if(blockNodeFacInstance == null)
                statements.Add(originalStatement);
            else { 
                statements = blockNodeFacInstance.ConvertBlockNode();
                if(blockNodeFacInstance.StatementReplacements != null)
                    statementReplacements.AddRange(blockNodeFacInstance.StatementReplacements);
            }

            return statements;
        }


        /// <summary>
        /// Conversion Rule applies the following:
        /// In Case of Option in query (e.g. from t in b) where b has multiplicity option
        /// 1.find the defining class of b
        /// 2.Let the class inherit from IEnumerable(classtype)
        /// 3.Implement the ienumerable interface in class
        ///     3.a content of this method will be a list which is initialised with (this element)
        ///     3.b the enumerator of this list will be returned
        /// </summary>
        /// <param name="newBlock">TODO:Check if required</param>
        /// <param name="nodeToReplace">Infos about the node which will be replaced</param>
        /// <returns></returns>
        //private List<StatementSyntax> ConvertQueryExpression(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    var queryStmt = (StatementSyntax) nodeToReplaceInBlock.Statement;
        //    var identifierName = (IdentifierNameSyntax)nodeToReplaceInBlock.ReferencedNode.DescendantNodes().First(p => p is IdentifierNameSyntax);
            
        //    if (identifierName == null) throw new Exception("Could not find Identifier in Linq Statement");
            
        //    CommonSyntaxNode decleration = Utilities.FindDecleration(_semanticModel, identifierName);
        //    TypeSyntax typeOfDeclaration = null;
        //    if(decleration is FieldDeclarationSyntax)
        //        typeOfDeclaration = (TypeSyntax)((FieldDeclarationSyntax)decleration).Declaration.DescendantNodes().First(p => p is TypeSyntax);
   
        //    if (decleration is PropertyDeclarationSyntax)
        //        typeOfDeclaration = ((PropertyDeclarationSyntax)decleration).Type;
        //        //TODO: Also Properties needs to be added see empty if
            
        //    string linqVariable = VariableCreatorService.GetVariable();
            
        //    var dummyList = Syntax.ParseStatement(string.Format("System.Collections.Generic.List<{0}> {1} = new System.Collections.Generic.List<{0}>() {{ {2} }};" + 
        //        Environment.NewLine,
        //        typeOfDeclaration.ToString(), linqVariable, identifierName.ToString()));

        //    var newIdentifier = Syntax.IdentifierName(linqVariable);
        //    queryStmt = queryStmt.ReplaceNode(identifierName, newIdentifier);
        //    queryStmt = queryStmt.WithLeadingTrivia(Syntax.Space);
        //    queryStmt = queryStmt.WithTrailingTrivia(Syntax.Space);
        //    statementsToAdd.AddRange(dummyList, queryStmt);
        
        //    return statementsToAdd;

        //}

        ///// <summary>
        ///// Applys down or upcast depending on the desired cast
        ///// </summary>
        ///// <param name="blockSyntax">the block where thecast has been found</param>
        ///// <param name="nodeToReplaceInBlock">information about the node to replace</param>
        ///// <returns>list of statements</returns>
        //private List<StatementSyntax> ApplyCast(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    //TODO: if time left switch to fuly qualified name
        //    var typeSymbol = Utilities.GetTypeInformation(_semanticModel, nodeToReplaceInBlock.ReferencedNode);
        //    INamedTypeSymbol multiplicityTypeSymbol = _semanticModel.Compilation.GetTypeByMetadataName("Roslyn.Compiler.Extension.Types.IMultiplicityAttribute");

        //    //We deal with a multiplicity cast - Apply M-Cast rules
        //    if (typeSymbol.Interfaces.Contains(multiplicityTypeSymbol))
        //    {

        //        var typeToCastTo = (nodeToReplaceInBlock.ReferencedNode as CastExpressionSyntax).Type;
        //        var anyAttributeType = Syntax.ParseName("AnyAttribute");

        //        if (anyAttributeType.EquivalentTo(typeToCastTo))
        //            statementsToAdd = ApplyUpCast(blockSyntax, nodeToReplaceInBlock);
        //        else
        //            statementsToAdd = ApplyDownCast(blockSyntax, nodeToReplaceInBlock);
        //    }
        //    //We deal with a type cast - Apply R-TCast Rule
        //    else
        //    {
        //        statementsToAdd = ApplyTypeCast(blockSyntax, nodeToReplaceInBlock);
        //    }
        
        //    return statementsToAdd;

        //}

        //private List<StatementSyntax> ApplyTypeCast(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    var castExpression = (CastExpressionSyntax)nodeToReplaceInBlock.ReferencedNode;

        //    var variableToCast = (IdentifierNameSyntax)castExpression.Expression.DescendantNodesAndSelf().First(n => n is IdentifierNameSyntax);
        //    var declaration = Utilities.FindDecleration(_semanticModel, variableToCast);
        //    AttributeType multiplicityType = AttributeType.None;
        //    GenericNameSyntax genericName = null; 
        //    Utilities.GetMultiplicityAttribute(declaration, out multiplicityType, out genericName);
        //    var castedGenericType = Utilities.ConvertAnyType(castExpression.Type, genericName);
           
      
        //    if (multiplicityType == AttributeType.Any)
        //    {
        //        string listDummyVar = VariableCreatorService.GetVariable();
        //        string loopVar = VariableCreatorService.GetVariable();
                
        //        var dummyListStmt = Syntax.ParseStatement(string.Format("{0} {1} = new {0}();"+ Environment.NewLine,castedGenericType,listDummyVar));
        //        var foreachLoop = Syntax.ParseStatement(string.Format("foreach(var {0} in {1}) {2}.Add(({3}){0}) ;" + Environment.NewLine,
        //            loopVar, variableToCast, listDummyVar, castExpression.Type));
        //        var newAssignExpression = Syntax.ParseExpression(listDummyVar + ";");

        //        var newStatement = nodeToReplaceInBlock.Statement.ReplaceNode(castExpression, newAssignExpression);
        //        newStatement = newStatement.WithLeadingTrivia(Syntax.Space);
        //        newStatement = newStatement.WithTrailingTrivia(Syntax.Space);
        //        statementsToAdd.AddRange(dummyListStmt, foreachLoop, newStatement);
        //    }
        //    else
        //    {
        //        statementsToAdd.Add(nodeToReplaceInBlock.Statement);
        //    }
        //    return statementsToAdd;
        //}


        ///// <summary>
        ///// Implements down cast from multiplicity any to another multiplicity less than any
        ///// Basically applys rule R-MCast1. But the implementation was based on the thought of Steimann Document Onward2013
        ///// </summary>
        ///// <param name="blockSyntax"></param>
        ///// <param name="nodeToReplaceInBlock"></param>
        ///// <returns></returns>
        //private List<StatementSyntax> ApplyDownCast(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();

        //    CastExpressionSyntax multiplicityCastExp = (CastExpressionSyntax)nodeToReplaceInBlock.ReferencedNode;
        //    var typeCast = (CastExpressionSyntax)multiplicityCastExp.DescendantNodes().First(n => n is CastExpressionSyntax);
           
        //    string typeOfExpression = typeCast.Type.ToString();
        //    string expressionVariable = VariableCreatorService.GetVariable();
            
        //    var expressionSyntax = multiplicityCastExp.Expression;
        //    var newResult = VariableCreatorService.GetVariable();

        //    var variableDeclaration = Syntax.ParseStatement(string.Format("{0} {1};" + Environment.NewLine, typeOfExpression, newResult));
        //    var assignExpr = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, expressionVariable, typeCast.Expression));
        //    var conditionStmt = Syntax.ParseStatement(string.Format(
        //        "if({0}.Count == 0) {{ {1} = null; }}{2} " +
        //        "else if({0}.Count == 1) {{ {3} = {0}.First();{2} }}"+
        //        "else throw new MultiplicityCastException(\"Multiplicity Exception\");{2}", expressionVariable, newResult, Environment.NewLine, newResult));
        //    var newAssignExpression = Syntax.ParseExpression(newResult + ";");
        //    var newAssignStmt = nodeToReplaceInBlock.Statement.ReplaceNode(nodeToReplaceInBlock.ReferencedNode, newAssignExpression);
        //    newAssignStmt = newAssignStmt.WithLeadingTrivia(Syntax.Space);
        //    newAssignStmt = newAssignStmt.WithTrailingTrivia(Syntax.Space);
        //    statementsToAdd.AddRange(variableDeclaration, assignExpr, conditionStmt, newAssignStmt);

        //    return statementsToAdd;
        //}

        ///// <summary>
        ///// Applies the Upcast from bare, option etc. to multiplicity any 
        ///// Implements the rules for R-MCast2 and R-MCast3
        ///// </summary>
        ///// <param name="blocksyntax">the block where the cast shall take place</param>
        ///// <param name="nodeToReplaceInBlock">the node to replace in the block</param>
        ///// <returns>upcasted node</returns>
        //private List<StatementSyntax> ApplyUpCast(BlockSyntax blocksyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    var multiplicityCast = ((CastExpressionSyntax)nodeToReplaceInBlock.ReferencedNode);
        //    var collectionCast = ((CastExpressionSyntax)multiplicityCast.Expression);
        //    var typeInfo = _semanticModel.GetTypeInfo(collectionCast.Expression);


        //    var variableToCast = (IdentifierNameSyntax)collectionCast.Expression.DescendantNodesAndSelf().First(n => n is IdentifierNameSyntax);
        //    var declaration = Utilities.FindDecleration(_semanticModel, variableToCast);
        //    var attributeType = Utilities.GetMultiplicityAttribute(declaration);


        //    //Apply Rule R-MCast2
        //    if (attributeType == AttributeType.Any)
        //    {
        //        var newExpression = Syntax.ParseExpression(string.Format("new {0}<{1}>() {{{2}}};", collectionCast.Type.ToString(),
        //            typeInfo.Type, collectionCast.Expression.ToString()));
        //        statementsToAdd.Add(nodeToReplaceInBlock.Statement.ReplaceNode(nodeToReplaceInBlock.ReferencedNode, newExpression));
        //    }
        //    //Apply Rule R-MCast3
        //    else
        //    {
        //        string listVariable = VariableCreatorService.GetVariable();
        //        string tempVariable = VariableCreatorService.GetVariable();
        //        var initList = Syntax.ParseStatement(string.Format("{0}<{1}> {2} = new {0}<{1}>();" + Environment.NewLine,
        //            collectionCast.Type.ToString(), typeInfo.Type, listVariable));
        //        var storeVariable = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, tempVariable, collectionCast.Expression));
        //        var condition = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine, tempVariable, listVariable));
        //        var newAssignExpression = Syntax.ParseExpression(listVariable + ";");
        //        var newCastStatement = nodeToReplaceInBlock.Statement.ReplaceNode(nodeToReplaceInBlock.ReferencedNode, newAssignExpression);
        //        newCastStatement = newCastStatement.WithLeadingTrivia(Syntax.Space);
        //        newCastStatement = newCastStatement.WithTrailingTrivia(Syntax.Space);
        //        statementsToAdd.AddRange(initList, storeVariable, condition, newCastStatement);
        //    }
            

        //    return statementsToAdd;
        //}


        /// <summary>
        /// Implements rules 
        ///     R-Return1 -> Method is never called when multiplicity of method is less than any. 
        ///                  If it is any and the generic types of method and returned elements are the same a copy of the list is returned
        ///                  otherwise use r-return2
        ///     R-Return2 -> if method has multiplicity any(c) and return value is not any(c)
        ///                  Casts the return value basically to any multiplicity and checks if null
        /// </summary>
        /// <param name="blockSyntax"></param>
        /// <param name="nodeToReplaceInBlock"></param>
        /// <returns></returns>
        //private List<StatementSyntax> ConvertReturnStatement(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    var returnStatement = Utilities.CastNode<ReturnStatementSyntax>(nodeToReplaceInBlock.ReferencedNode);
        //    var methodDeclaration = nodeToReplaceInBlock.Statement.FirstAncestorOrSelf<MethodDeclarationSyntax>();
        //    GenericNameSyntax genericTypeMethod = null;
        //    AttributeType methodAttributeType = AttributeType.None;
        //    Utilities.GetMultiplicityAttribute(methodDeclaration, out methodAttributeType, out genericTypeMethod);
           
        //    var expression = ((ReturnStatementSyntax)nodeToReplaceInBlock.ReferencedNode).Expression;
        //    if (expression != null)
        //    {
        //        var identifier = (IdentifierNameSyntax)expression.DescendantNodesAndSelf().First(n => n is IdentifierNameSyntax);
        //        var returnValueDeclaration = Utilities.FindDecleration(_semanticModel, identifier);
        //        AttributeType returnValueAttributeType = Utilities.GetMultiplicityAttribute(returnValueDeclaration);
        //        //Implementation of R-Return2 (return option or bare as if casted to any)
        //        //TODO: Refactor to a method as same logic will be used later
        //        if (methodAttributeType == AttributeType.Any && returnValueAttributeType != AttributeType.Any)
        //        {
        //            var expressionVariable = VariableCreatorService.GetVariable();
        //            var newVariable = VariableCreatorService.GetVariable();
        //            var saveExpressionStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, expressionVariable, expression));
        //            var initListStmt = Syntax.ParseStatement(string.Format("var {0} = new {1}();"+ Environment.NewLine, newVariable, genericTypeMethod.ToString()));

        //            var condStmt = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine, expressionVariable, newVariable));
        //            var returnStmt = Syntax.ParseStatement(string.Format("return {0};"+ Environment.NewLine, newVariable));

        //            statementsToAdd.AddRange(saveExpressionStatement,initListStmt, condStmt, returnStmt);
                    
        //        }
        //        //Part of the rule R-Return1
        //        else if (methodAttributeType == AttributeType.Any && returnValueAttributeType == AttributeType.Any)
        //        {
        //            StatementSyntax returnStmt = null;
        //            GenericNameSyntax genericTypeIdentifier = null;
        //            Utilities.GetMultiplicityAttribute(returnValueDeclaration, out returnValueAttributeType, out genericTypeIdentifier);

        //            //Apply R-Return1 if generic types are identical
        //            if (genericTypeIdentifier.IsEquivalentTo(genericTypeMethod))
        //                returnStmt = Syntax.ParseStatement(string.Format("return new {0}({1});" + Environment.NewLine,
        //                genericTypeMethod.ToString(), identifier.ToString()));
        //            //Apply R-Return2 if generic types are not identical
        //            else
        //            {
        //                var expressionVariable = VariableCreatorService.GetVariable();
        //                var newVariable = VariableCreatorService.GetVariable();
        //                var saveExpressionStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, expressionVariable, expression));
        //                var initListStmt = Syntax.ParseStatement(string.Format("var {0} = new {1}();" + Environment.NewLine, newVariable, genericTypeMethod.ToString()));

        //                var condStmt = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine, expressionVariable, newVariable));
        //                returnStmt = Syntax.ParseStatement(string.Format("return {0};" + Environment.NewLine, newVariable));

        //                statementsToAdd.AddRange(saveExpressionStatement, initListStmt, condStmt);
        //            }

        //            statementsToAdd.Add(returnStmt);
        //        }
        //        else
        //        {
        //            return null;
        //        }
        //    }
        //    else
        //    {
        //        return null;
        //    }
        //    return statementsToAdd;
        //}

        //private List<StatementSyntax> ConvertInvocationExpression(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
           
        //    var invocationExpression = Utilities.CastNode<InvocationExpressionSyntax>(nodeToReplaceInBlock.ReferencedNode);
        //    var newStmt = nodeToReplaceInBlock.Statement;
        //    var leadingTrivia = newStmt.GetLeadingTrivia();
            

        //    //Check the Arguments
        //    if (invocationExpression.ArgumentList.Arguments.Count == 0)
        //        return null;
        //    ArgumentListSyntax argumentList = Syntax.ParseArgumentList("()");
        //  //  if (invocationExpression.ArgumentList.Arguments == null) argumentList = invocationExpression.ArgumentList;

        //    foreach (var argument in invocationExpression.ArgumentList.Arguments)
        //    {
        //        if (argument.Expression is IdentifierNameSyntax)
        //        {
        //            var argumentIdentfier = Utilities.CastNode<IdentifierNameSyntax>(argument.Expression);
        //            CommonSyntaxNode argumentDefinition = Utilities.FindDecleration(_semanticModel, argumentIdentfier);
        //            if (argumentDefinition != null)
        //            {
        //                AttributeType argumentDefinitionMultiplicity = AttributeType.None;
        //                GenericNameSyntax genericType = null;
        //                Utilities.GetMultiplicityAttribute(argumentDefinition, out argumentDefinitionMultiplicity, out genericType);
        //                var roslynVariable = VariableCreatorService.GetVariable();
        //                //Realizes Parameter Passes
        //                if (argumentDefinitionMultiplicity == AttributeType.Any || argumentDefinitionMultiplicity == AttributeType.Option)
        //                {
        //                    StatementSyntax initList = null;
        //                    StatementSyntax assignList = null;
        //                    switch (argumentDefinitionMultiplicity)
        //                    {
        //                        case AttributeType.Any:
        //                            initList = Syntax.ParseStatement(string.Format("{0} {1} = new {0}();" + Environment.NewLine, genericType.ToString(), roslynVariable));
        //                            break;
        //                        case AttributeType.Option:
        //                            VariableDeclarationSyntax declaredVariable= 
        //                                (VariableDeclarationSyntax) argumentDefinition.DescendantNodes().First(n => n is VariableDeclarationSyntax);
        //                            initList = Syntax.ParseStatement(string.Format("{0} {1};" + Environment.NewLine,declaredVariable.Type.ToString(), roslynVariable));
        //                            break;
        //                    }
        //                    initList = initList.WithLeadingTrivia(nodeToReplaceInBlock.Statement.GetLeadingTrivia());
        //                    //TODO: Needs to be translated
        //                    assignList = Syntax.ParseStatement(string.Format("{0} = {1};" + Environment.NewLine, roslynVariable, argumentIdentfier.ToString()));
        //                    var newArgument = Syntax.ParseArgumentList(roslynVariable);
        //                    argumentList = argumentList.AddArguments(newArgument.Arguments[0]);
        //                    // argumentList = argumentList.ReplaceNode(argument, newArgument.Arguments[0]);
        //                    leadingTrivia = Syntax.ParseLeadingTrivia(string.Empty);
        //                    statementsToAdd.AddRange(initList, assignList);

        //                }
        //                else { argumentList = argumentList.AddArguments(argument); }

        //            }
        //            else { argumentList = argumentList.AddArguments(argument); }


        //        }
        //        else { argumentList = argumentList.AddArguments(argument); }

        //    }
        //    newStmt = newStmt.ReplaceNode(invocationExpression.ArgumentList, argumentList);
        //    statementsToAdd.Add(newStmt);

        //    return statementsToAdd;
        //}

        ///// <summary>
        ///// Implements R-MembAcc1, R-MembAcc2 and R-MembAcc3
        ///// </summary>
        ///// <param name="blockSyntax"></param>
        ///// <param name="nodeToReplaceInBlock"></param>
        ///// <returns></returns>
        //private List<StatementSyntax> ConvertMemberAccessExpression(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();

        //    var memberAccessExpression = Utilities.CastNode<MemberAccessExpressionSyntax>(nodeToReplaceInBlock.ReferencedNode);
            
        //    AttributeType rightSideMultiplicity = AttributeType.None;
        //    GenericNameSyntax genericType = null;


        //    //check the multiplicities of left and right side node
        //  //  var rightSideDecl = Utilities.GetMemberDecleration(_semanticModel, memberAccessExpression);
        //    var leftSide = memberAccessExpression.Expression.FirstAncestorOrSelf<SimpleNameSyntax>();
        //    var rightSide = memberAccessExpression.Name.FirstAncestorOrSelf<SimpleNameSyntax>();
        //    if (leftSide != null && rightSide != null)
        //    {
        //        var leftSideNode = Utilities.FindDecleration(_semanticModel, leftSide);
        //        var rightSideNode = Utilities.FindDecleration(_semanticModel, rightSide);
        //        var leftSideMultilicity = Utilities.GetMultiplicityAttribute(leftSideNode);
        //        Utilities.GetMultiplicityAttribute(rightSideNode, out rightSideMultiplicity, out genericType);
        //        var type = Utilities.GetType(rightSideNode);
        //        var member = (MemberDeclarationSyntax)rightSideNode;


        //        string assignVariable = VariableCreatorService.GetVariable();

        //        //case R-MembAcc1 (field is option)
        //        if (leftSideMultilicity == AttributeType.Option &&
        //            (rightSideMultiplicity == AttributeType.Any || rightSideMultiplicity == AttributeType.Option))
        //        {
        //            var checkNullStmt = Syntax.ParseStatement(string.Format("var {0} = {1} != null ? {2} : null;" + Environment.NewLine,
        //                assignVariable, memberAccessExpression.Expression.ToString(), memberAccessExpression.ToString()));
        //            checkNullStmt = checkNullStmt.WithLeadingTrivia(nodeToReplaceInBlock.Statement.GetLeadingTrivia());
        //            statementsToAdd.Add(checkNullStmt);
        //        }
        //        else
        //        {

        //            string loopVariable = VariableCreatorService.GetVariable();
        //            StatementSyntax listInit = null, foreachLoop = null;
        //            //case R-MembAcc2
        //            if (leftSideMultilicity == AttributeType.Any && rightSideMultiplicity == AttributeType.Option)
        //            {
        //                listInit = Syntax.ParseStatement(string.Format("List<{0}> {1} = new List<{0}>();" + Environment.NewLine,
        //                    type.ToString(), assignVariable));
        //                listInit = listInit.WithLeadingTrivia(nodeToReplaceInBlock.Statement.GetLeadingTrivia());
        //                //Für properties sonderfall berücksichtigen (siehe auch Binkle)
        //                foreachLoop = Syntax.ParseStatement(string.Format("foreach(var {0} in {1}){{ if({0}.{3} != null) {2}.Add({0}.{3});}}" + Environment.NewLine,
        //                    loopVariable, memberAccessExpression.Expression.ToString(), assignVariable, rightSide.ToString()));
        //            }
        //            //case R-MembAcc3
        //            else if (leftSideMultilicity == AttributeType.Any && rightSideMultiplicity == AttributeType.Any)
        //            {
        //                listInit = Syntax.ParseStatement(string.Format("List<{0}> {1} = new List<{0}>();" + Environment.NewLine,
        //                    genericType.TypeArgumentList.Arguments.First(), assignVariable));
        //                listInit = listInit.WithLeadingTrivia(nodeToReplaceInBlock.Statement.GetLeadingTrivia());

        //                foreachLoop = Syntax.ParseStatement(string.Format("foreach(var {0} in {1}) {2}.AddRange({0}.{3});" + Environment.NewLine,
        //                    loopVariable, memberAccessExpression.Expression.ToString(), assignVariable, rightSide.ToString()));

        //            }
        //            statementsToAdd.Add(listInit);
        //            statementsToAdd.Add(foreachLoop);
        //        }

        //        //Assign the new variable (the return value in skript) to the left handed side
        //        var oldequalsValueClause = memberAccessExpression.FirstAncestorOrSelf<EqualsValueClauseSyntax>();
        //        if (oldequalsValueClause != null)
        //        {
        //            var newEqualsValueClause = oldequalsValueClause.WithValue(Syntax.ParseExpression(assignVariable));
        //            var newAssignStmt = nodeToReplaceInBlock.Statement.ReplaceNode(oldequalsValueClause, newEqualsValueClause);
        //            newAssignStmt = newAssignStmt.WithLeadingTrivia(Syntax.Space);
        //            newAssignStmt = newAssignStmt.WithTrailingTrivia(nodeToReplaceInBlock.Statement.GetTrailingTrivia());
        //            statementsToAdd.Add(newAssignStmt);

        //        }

        //    }
        //    else { return null; }

        //    return statementsToAdd;
        //}

        //#region Convert Binary Expression
        ///// <summary>
        ///// TODO: R-MembAcc1 ? Check if left side is option and if null????
        ///// R-MembAcc2
        ///// Converts the Binary Expression 
        ///// </summary>
        ///// <param name="blockSyntax"></param>
        ///// <param name="nodeToReplaceInBlock"></param>
        ///// <returns></returns>
        //private List<StatementSyntax> ConvertBinaryExpression(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
            
        //   // var expressionStatement = (ExpressionStatementSyntax)nodeToReplaceInBlock.Statement;
        //    var binaryExpression = nodeToReplaceInBlock.ReferencedNode as BinaryExpressionSyntax;
        //    if (!(binaryExpression.Left is IdentifierNameSyntax) || !(binaryExpression.Right is ExpressionSyntax)) 
        //        return null;

        //    if (binaryExpression.Kind == SyntaxKind.EqualsExpression)
        //        return ConvertEqualsExpression(blockSyntax, nodeToReplaceInBlock);

        //    //First check the leftside of the node for the multiplicity. If it is not Any, we can return
        //    var leftIdentifier = (IdentifierNameSyntax)binaryExpression.Left;
        //    var leftSideNode = Utilities.FindDecleration(_semanticModel, leftIdentifier);
        //    var leftSideMultilicity = Utilities.GetMultiplicityAttribute(leftSideNode);
        //    if (leftSideMultilicity != AttributeType.Any) return null;

        //    //Now check the right side. Depending on the multiplicity of the right side we will choose a proper replacement
        //    ExpressionSyntax expression = (ExpressionSyntax)binaryExpression.Right;
        //    CommonSyntaxNode declerationNode = null;
        //    if (expression is IdentifierNameSyntax)
        //        declerationNode = Utilities.FindDecleration(_semanticModel, (IdentifierNameSyntax)expression);
        //    var multiplicityType = Utilities.GetMultiplicityAttribute(declerationNode);

        //    //Now start with the transformation
        //    //In Case of add assign += we don't want to delete the list, but we want to add the item
        //    if (nodeToReplaceInBlock.ReferencedNode.Kind == SyntaxKind.AddAssignExpression)
        //    {
        //        statementsToAdd = ConvertAddAssignExpression(blockSyntax, multiplicityType, leftIdentifier, expression);
        //    }
        //    //Here we remove the item from the list
        //    else if (nodeToReplaceInBlock.ReferencedNode.Kind == SyntaxKind.SubtractAssignExpression)
        //    {
        //        statementsToAdd = ConvertSubtractAssignExpression(blockSyntax, multiplicityType, leftIdentifier, expression);
        //    }
        //    //and here we basically clear the list and then add the same code as in addassignexpression
        //    else
        //    {
        //        string tempVariable = VariableCreatorService.GetVariable();
        //        StatementSyntax clearStatement = Syntax.ParseStatement(string.Format("{0}.Clear();" + Environment.NewLine, leftIdentifier.ToFullString()));     
        //        statementsToAdd.Add(clearStatement);
        //        statementsToAdd.AddRange(ConvertAddAssignExpression(blockSyntax, multiplicityType, leftIdentifier, expression));
        //    }

        //    return statementsToAdd;
            
        //}

        ///// <summary>
        ///// Implements the rules for R-Ident1, R-Ident2, R-Ident3 and R-Ident4
        ///// </summary>
        ///// <param name="blockSyntax"></param>
        ///// <param name="nodeToReplaceInBlock"></param>
        ///// <returns></returns>
        //private List<StatementSyntax> ConvertEqualsExpression(BlockSyntax blockSyntax, SyntaxNodeInfo nodeToReplaceInBlock)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    var binaryExpression = nodeToReplaceInBlock.ReferencedNode as BinaryExpressionSyntax;
            
        //    //First check the leftside of the node for the multiplicity. If it is not Any, we can return
        //    var leftIdentifier = (IdentifierNameSyntax)binaryExpression.Left;
        //    var leftSideNode = Utilities.FindDecleration(_semanticModel, leftIdentifier);
        //    var leftSideMultiplicity = Utilities.GetMultiplicityAttribute(leftSideNode);

        //    //Secondly check the right side of the node for multiplicity
        //    var rightIdentifier = (IdentifierNameSyntax)binaryExpression.Right;
        //    var rightSideNode = Utilities.FindDecleration(_semanticModel, rightIdentifier);
        //    var rightSideMultiplicity = Utilities.GetMultiplicityAttribute(rightSideNode);

        //    var result = VariableCreatorService.GetVariable();
        //    var newIdentifier = Syntax.IdentifierName(result);
        //    var tempLeft = VariableCreatorService.GetVariable();
        //    var tempRight = VariableCreatorService.GetVariable();
        //    //R-Ident1
        //    if (leftSideMultiplicity == AttributeType.Option && rightSideMultiplicity == AttributeType.Option)
        //    {
        //        statementsToAdd.Add(nodeToReplaceInBlock.Statement);
        //    }
        //    else
        //    {
        //        var assignTempLeft = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, tempLeft, binaryExpression.Left));
        //        var assignTempRight = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, tempRight, binaryExpression.Right));
        //        StatementSyntax assignResult;
        //        if (leftSideMultiplicity == AttributeType.Any && rightSideMultiplicity == AttributeType.Option)
        //        {
        //            //@Text : IsEmpty does not exist, but we could use first with the same effect. Has also the advantage that we don't need to clone
        //            //var assignResult = Syntax.ParseStatement(string.Format("{0}.Count == 0 ? {1} == null : {0}.Clone().Remove({1})" + Environment.NewLine,
        //            //    tempLeft,tempRight));
        //            assignResult = Syntax.ParseStatement(string.Format("var {0} = {1}.Count == 0 ? {2} == null : {1}.First() == {2};" + Environment.NewLine,
        //                result, tempLeft, tempRight));
        //        }
        //        else if (leftSideMultiplicity == AttributeType.Option && rightSideMultiplicity == AttributeType.Any)
        //        {
        //            assignResult = Syntax.ParseStatement(string.Format("var {0} = {1}.Count == 0 ? {2} == null : {1}.First() == {2};" + Environment.NewLine,
        //                result, tempRight, tempLeft));
        //        }
        //        else
        //        {
        //            //@Text : Here the Linq Method Except is used instead of cloning and deleting the list
        //            assignResult = Syntax.ParseStatement(string.Format("var {0} = {1}.Count == {2}.Count && {1}.Except({2}).Count() == 0; " + Environment.NewLine,
        //                result, tempLeft, tempRight));
        //        }

        //        var newStatement = nodeToReplaceInBlock.Statement.ReplaceNode(nodeToReplaceInBlock.ReferencedNode, newIdentifier);
        //        newStatement = newStatement.WithLeadingTrivia(Syntax.Space);
        //        newStatement = newStatement.WithTrailingTrivia(Syntax.Space);
        //        statementsToAdd.AddRange(assignTempLeft, assignTempRight, assignResult, newStatement);
        //    }
        //    return statementsToAdd;


        //}

        ///// <summary>
        ///// Implements rule (M-SUB1)
        ///// </summary>
        ///// <param name="newBlock">the block where the statements are added</param>
        ///// <param name="rightSideMultiplicity">multiplicity of the right side </param>
        ///// <param name="leftIdentifier">identifier of the left side (the a)</param>
        ///// <param name="rightExpression">Expression of the right side (the b or a constuctor call for instance)</param>
        ///// <returns>list of statements which will be replaced</returns>
        //private static List<StatementSyntax> ConvertSubtractAssignExpression(BlockSyntax newBlock, AttributeType rightSideMultiplicity,
        //    IdentifierNameSyntax leftIdentifier, ExpressionSyntax rightExpression)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();
        //    string loopVariable = VariableCreatorService.GetVariable();
        //    if (rightSideMultiplicity == AttributeType.Any)
        //    {
        //        StatementSyntax removeLoop = Syntax.ParseStatement(string.Format("foreach (var {0} in {1}) {2}.Remove({0});" + Environment.NewLine,
        //            loopVariable,rightExpression.ToString(), leftIdentifier.ToString() ));
        //        statementsToAdd.Add(removeLoop);
        //    }
        //    //Option, Bare te
        //    else
        //    {
        //        string newVariable = VariableCreatorService.GetVariable();
        //        StatementSyntax initializeStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, newVariable, rightExpression.ToString()));
        //        StatementSyntax conditionStatement = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Remove({0});" + Environment.NewLine,
        //            newVariable, leftIdentifier.ToString()));
        //        statementsToAdd.Add(initializeStatement);
        //        statementsToAdd.Add(conditionStatement);

        //    }
        //    return statementsToAdd;
        //}

        ///// <summary>
        ///// (M-ADD1)
        ///// Applys the rules to add a new element aka a += b
        ///// </summary>
        ///// <param name="newBlock">the block where the statements are added</param>
        ///// <param name="rightSideMultiplicity">multiplicity of the right side </param>
        ///// <param name="leftIdentifier">identifier of the left side (the a)</param>
        ///// <param name="rightExpression">Expression of the right side (the b or a constuctor call for instance)</param>
        ///// <returns>list of statements which will be replaced</returns>
        //private static List<StatementSyntax> ConvertAddAssignExpression(BlockSyntax newBlock, AttributeType rightSideMultiplicity, 
        //    IdentifierNameSyntax leftIdentifier, ExpressionSyntax rightExpression)
        //{
        //    var statementsToAdd = new List<StatementSyntax>();

        //    //If we are accessing an "any" field on the right side we want to addrange otherwie we just want to check the element and add it
        //    if (rightSideMultiplicity == AttributeType.Any)
        //    {
        //        StatementSyntax addRangeStatement = Syntax.ParseStatement(string.Format("{0}.AddRange({1});" + Environment.NewLine,
        //            leftIdentifier.ToString(), rightExpression.ToString()));
        //        statementsToAdd.Add(addRangeStatement);
        //    }
        //    //Option, Bare te
        //    else
        //    {
        //        string newVariable = VariableCreatorService.GetVariable();
        //        StatementSyntax initializeStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, newVariable, rightExpression.ToString()));
        //        StatementSyntax conditionStatement = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine,
        //            newVariable, leftIdentifier.ToString()));
        //        statementsToAdd.AddRange(initializeStatement, conditionStatement);
                
        //    }
        //    return statementsToAdd;
        //}
        //#endregion
    }
}
